package com.lalilu.lmedia.indexer

import com.lalilu.lmedia.entity.Item
import com.lalilu.lmedia.entity.LAlbum
import com.lalilu.lmedia.entity.LArtist
import com.lalilu.lmedia.entity.LDictionary
import com.lalilu.lmedia.entity.LGenre
import com.lalilu.lmedia.entity.LSong
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseLibrary : CoroutineScope {
    override val coroutineContext: CoroutineContext = Dispatchers.IO

    private val songsFlow = MutableSharedFlow<Map<String, LSong>>(replay = 1)
    private val albumsFlow = MutableSharedFlow<Map<String, LAlbum>>(replay = 1)
    private val artistsFlow = MutableSharedFlow<Map<String, LArtist>>(replay = 1)
    private val genresFlow = MutableSharedFlow<Map<String, LGenre>>(replay = 1)
    private val dictionariesFlow = MutableSharedFlow<Map<String, LDictionary>>(replay = 1)

    internal var songsFlowCache: Map<String, LSong> = emptyMap()
        set(value) {
            field = value
            launch { songsFlow.emit(field) }
        }
    internal var albumsFlowCache: Map<String, LAlbum> = emptyMap()
        set(value) {
            field = value
            launch { albumsFlow.emit(field) }
        }
    internal var artistsFlowCache: Map<String, LArtist> = emptyMap()
        set(value) {
            field = value
            launch { artistsFlow.emit(field) }
        }
    internal var genresFlowCache: Map<String, LGenre> = emptyMap()
        set(value) {
            field = value
            launch { genresFlow.emit(field) }
        }
    internal var dictionariesFlowCache: Map<String, LDictionary> = emptyMap()
        set(value) {
            field = value
            launch { dictionariesFlow.emit(field) }
        }

    fun getSongs() = songsFlowCache.values.toList()
    fun getAlbums() = albumsFlowCache.values.toList()
    fun getArtists() = artistsFlowCache.values.toList()
    fun getGenres() = genresFlowCache.values.toList()
    fun getDictionaries() = dictionariesFlowCache.values.toList()

    fun getSongOrNull(id: String?, blockFilter: Boolean = true): LSong? =
        songsFlowCache[id]?.blockFilter(enable = blockFilter)

    fun getAlbumOrNull(id: String?, blockFilter: Boolean = true): LAlbum? =
        albumsFlowCache[id]?.blockFilter(enable = blockFilter)

    fun getArtistOrNull(id: String?, blockFilter: Boolean = true): LArtist? =
        artistsFlowCache[id]?.blockFilter(enable = blockFilter)

    fun getGenreOrNull(id: String?, blockFilter: Boolean = true): LGenre? =
        genresFlowCache[id]?.blockFilter(enable = blockFilter)

    fun getDictionaryOrNull(id: String?, blockFilter: Boolean = true): LDictionary? =
        dictionariesFlowCache[id]?.blockFilter(enable = blockFilter)

    fun getSongFlowById(mediaId: String?): SharedFlow<LSong?> =
        songsFlow.mapLatest { it[mediaId] }
            .shareIn(this, SharingStarted.Eagerly, 1)

    fun getArtistFlowById(artistName: String?): SharedFlow<LArtist?> =
        artistsFlow.mapLatest { it[artistName] }
            .shareIn(this, SharingStarted.Eagerly, 1)

    fun getAlbumFlowById(albumId: String?): SharedFlow<LAlbum?> =
        albumsFlow.mapLatest { it[albumId] }
            .shareIn(this, SharingStarted.Eagerly, 1)

    fun getDictionaryFlowById(dictionaryId: String?): SharedFlow<LDictionary?> =
        dictionariesFlow.mapLatest { it[dictionaryId] }
            .shareIn(this, SharingStarted.Eagerly, 1)

    fun getGenreFlowById(genreId: String?): SharedFlow<LGenre?> =
        genresFlow.mapLatest { it[genreId] }
            .shareIn(this, SharingStarted.Eagerly, 1)

    fun getSongsFlow(blockFilter: Boolean = true): Flow<List<LSong>> =
        songsFlow.mapLatest { it.values.blockFilter(blockFilter) }

    fun getAlbumsFlow(blockFilter: Boolean = true): Flow<List<LAlbum>> =
        albumsFlow.mapLatest { it.values.blockFilter(blockFilter) }

    fun getArtistsFlow(blockFilter: Boolean = true): Flow<List<LArtist>> =
        artistsFlow.mapLatest { it.values.blockFilter(blockFilter) }

    fun getGenresFlow(blockFilter: Boolean = true): Flow<List<LGenre>> =
        genresFlow.mapLatest { it.values.blockFilter(blockFilter) }

    fun getDictionariesFlow(blockFilter: Boolean = true): Flow<List<LDictionary>> =
        dictionariesFlow.mapLatest { it.values.blockFilter(blockFilter) }

    private fun <T : Item> Collection<T>.blockFilter(enable: Boolean): List<T> =
        if (!enable) this.toList() else filter { !it.blocked }

    private fun <T : Item> T?.blockFilter(enable: Boolean): T? =
        if (!enable) this else this?.takeIf { !it.blocked }
}